package semant;


import java.util.Iterator;


import ast.*;

import util.ClassTreeNode;
import util.ErrorHandler;
import util.SymbolTable;


public class ClassEnvVisitor extends SemanticVisitor {
	
	ErrorHandler errorhandler;
	ClassTreeNode ctnode;
	SymbolTable vt; //Variable Table
	SymbolTable mt; //Method Table
	
	public ClassEnvVisitor(ClassTreeNode node,
							ErrorHandler errorHandler)
	{
		this.ctnode = node;
		this.vt = node.getVarSymbolTable();
		this.mt = node.getMethodSymbolTable();
		this.errorhandler = errorHandler;	
		vt.enterScope();
		mt.enterScope();
	}

    /** Visit a class node
      * @param node the class node
      * @return result of the visit 
      * */
    public Object visit(Class_ node) {
			node.getMemberList().accept(this);
		return null;
    }

    /** Visit a list node of members
      * @param node the member list node
      * @return result of the visit 
      * */
    public Object visit(MemberList node) {
		for (Iterator it = (Iterator) node.getIterator(); it.hasNext();)
		    ((Member)it.next()).accept(this);
		return null;
    }

    /** Visit a field node
      * @param node the field node
      * @return result of the visit 
      * */
    public Object visit(Field node) { 
		if(vt.peek(node.getName()) != null) {
			errorhandler.register(errorhandler.SEMANT_ERROR, 
                    ctnode.getASTNode().getFilename(), 
                    node.getLineNum(),
					"field '" + node.getName() + "'" +
					" is already defined in class '" + 
					ctnode.getName() + "'");
		} else if(reservedNameChecker(node.getName())){
			errorhandler.register(errorhandler.SEMANT_ERROR, 
                    ctnode.getASTNode().getFilename(), 
                    node.getLineNum(),
					"fields cannot be named '" + 
					node.getName() + "'");
		}
		vt.add("this." + node.getName(), node.getType());
		vt.add(node.getName(),node.getType());
		return null; 
    }

    /** Visit a method node
      * @param node the method node
      * @return result of the visit 
      * */
    public Object visit(Method node) { 
    	 if( mt.peek(node.getName()) != null) {
		ClassTreeNode c = ctnode.getParent();
		boolean found = false;
		while(c != null) {
			if(c.getMethodSymbolTable().peek(node.getName()) != null) {
				found = true;
				break;
			}
			c = c.getParent();
		}
		if(!found) {
			errorhandler.register(errorhandler.SEMANT_ERROR, 
		    ctnode.getASTNode().getFilename(), 
		    node.getLineNum(),
					"method '" + node.getName() + "'" +
					" is already defined in class '" + 
					ctnode.getName() + "'");
		}
 	   } else if(reservedNameChecker(node.getName())){
			errorhandler.register(errorhandler.SEMANT_ERROR, 
                    ctnode.getASTNode().getFilename(), 
                    node.getLineNum(),
					"methods cannot be named '" + 
					node.getName() + "'");
		}
		mt.add(node.getName(),
				node.getReturnType() + 
				"->" + 
				(String)node.getFormalList().accept(this));
		return null; 
    }
    
    public Object visit(FormalList node) {
    	String s = "";
    	for (Iterator it = (Iterator) node.getIterator(); it.hasNext();)
		    s += ((String)((Formal)it.next()).accept(this)) + "*";
    	if(s.length() != 0)
    		s = s.substring(0,s.length()-2);
    	return s;
    }
    
    public Object visit(Formal node) {
    	return node.getType();
    }
}